package editor.graphics;

import moltencore.game.loader.Level;
import moltencore.game.loader.LevelSprite;
import editor.logic.LevelSpriteFactory;
import editor.logic.Editor;
import spark.graphics.scenegraph.SceneGraph;
import spark.graphics.scenegraph.TranslationNode;
import spark.graphics.scenegraph.SpriteNode;
import spark.graphics.SpriteRenderable;
import spark.graphics.Renderer;
import spark.graphics.ISpriteMap;
import spark.exception.LoadingException;
import moltencore.game.bubble.Bubble;
import moltencore.game.bubble.Type;
import moltencore.game.collision.HorizontalLine;
import moltencore.game.collision.VerticalLine;

import javax.media.opengl.GL;
import java.util.HashMap;

public class LevelRenderer {
	private SceneGraph graph;
	private HashMap<Type, SpriteRenderable> bubbleRenderables = new HashMap<Type, SpriteRenderable>();
	private SpriteRenderable next = new SpriteRenderable();
	private SpriteRenderable nextNext = new SpriteRenderable();
	private SpriteRenderable score = new SpriteRenderable();
	private SpriteRenderable result = new SpriteRenderable();
	private SpriteRenderable nick = new SpriteRenderable();
	private SpriteRenderable clock = new SpriteRenderable();
	private SpriteRenderable push = new SpriteRenderable();
	private SpriteRenderable fill = new SpriteRenderable();
	private HashMap<String, LevelSpriteFactory> levelSpriteFactories;

	public LevelRenderer(SceneGraph graph, LevelSpriteFactory factory, HashMap<String, LevelSpriteFactory> levelSpriteFactories) throws LoadingException {
		this.graph = graph;
		this.levelSpriteFactories = levelSpriteFactories;
		try {
			SpriteRenderable renderable;
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_RED).getRenderable();
			bubbleRenderables.put(Type.Red, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_GREEN).getRenderable();
			bubbleRenderables.put(Type.Green, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_BLUE).getRenderable();
			bubbleRenderables.put(Type.Blue, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_YELLOW).getRenderable();
			bubbleRenderables.put(Type.Yellow, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_ORANGE).getRenderable();
			bubbleRenderables.put(Type.Orange, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_VIOLET).getRenderable();
			bubbleRenderables.put(Type.Violet, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_BLACK).getRenderable();
			bubbleRenderables.put(Type.Black, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_WHITE).getRenderable();
			bubbleRenderables.put(Type.White, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_RAINBOW).getRenderable();
			bubbleRenderables.put(Type.Rainbow, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_BLAZING).getRenderable();
			bubbleRenderables.put(Type.Blazing, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_STONE).getRenderable();
			bubbleRenderables.put(Type.Ghost, renderable);
			renderable = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_ANCHOR).getRenderable();
			bubbleRenderables.put(Type.Anchor, renderable);
			next = factory.getLevelSprite(PhysicalSpritePanel.NEXT).getRenderable();
			next.getPosition().z = Editor.OTHER_DEPTH;
			nick = factory.getLevelSprite(PhysicalSpritePanel.NICK).getRenderable();
			nick.getPosition().z = Editor.OTHER_DEPTH;
			clock = factory.getLevelSprite(PhysicalSpritePanel.CLOCK).getRenderable();
			clock.getPosition().z = Editor.OTHER_DEPTH;
			nextNext = factory.getLevelSprite(PhysicalSpritePanel.NEXT_NEXT).getRenderable();
			nextNext.getPosition().z = Editor.OTHER_DEPTH;
			score = factory.getLevelSprite(PhysicalSpritePanel.SCORE).getRenderable();
			score.getPosition().z = Editor.OTHER_DEPTH;
			result = factory.getLevelSprite(PhysicalSpritePanel.RESULT).getRenderable();
			result.getPosition().z = Editor.OTHER_DEPTH;
			push = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_PUSH).getRenderable();
			push.getPosition().z = Editor.OTHER_DEPTH;
			fill = factory.getLevelSprite(PhysicalSpritePanel.BUBBLE_FILL).getRenderable();
			fill.getPosition().z = Editor.OTHER_DEPTH;
		}
		catch(LoadingException e) {
			throw new LoadingException(e);
		}
	}

	public void renderLevel(Level level, boolean renderMisc, boolean renderPush, boolean renderFill) {
		for(LevelSprite sprite : level.getLevelSprites()) {
			if(sprite.getRenderable().getSprite() == null) {
				LevelSpriteFactory factory = levelSpriteFactories.get(sprite.getSpriteMap() + ":" + sprite.getTheme());
				ISpriteMap spriteMap = null;
				try {
					spriteMap = factory.getSpriteMap();
				}
				catch(LoadingException e) {
					throw new AssertionError(e);
				}
				sprite.getRenderable().setSprite(spriteMap.getSprite(sprite.getId()));
			}
			TranslationNode spriteTranslationNode = new TranslationNode(sprite.getRenderable().getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(sprite.getRenderable());
			spriteTranslationNode.addChild(spriteNode);
			graph.getRoot().addChild(spriteTranslationNode);
		}
		if(level.getAttachedBubbles() != null) {
			for(Bubble bubble : level.getAttachedBubbles()) {
				TranslationNode bubbleTranslationNode = new TranslationNode(bubble.getPosition());
				SpriteNode bubbleSpriteNode = new SpriteNode();
				bubbleSpriteNode.registerForRendering(bubbleRenderables.get(bubble.getType()));
				bubbleTranslationNode.addChild(bubbleSpriteNode);
				graph.getRoot().addChild(bubbleTranslationNode);
			}
		}
		if(renderPush && level.getPushBubbles() != null) {
			for(Bubble bubble : level.getPushBubbles()) {
				TranslationNode bubbleTranslationNode = new TranslationNode(bubble.getPosition());
				SpriteNode bubbleSpriteNode = new SpriteNode();
				bubbleSpriteNode.registerForRendering(push);
				bubbleTranslationNode.addChild(bubbleSpriteNode);
				graph.getRoot().addChild(bubbleTranslationNode);
			}
		}
		if(renderFill && level.getFillBubbles() != null) {
			for(Bubble bubble : level.getFillBubbles()) {
				TranslationNode bubbleTranslationNode = new TranslationNode(bubble.getPosition());
				SpriteNode bubbleSpriteNode = new SpriteNode();
				bubbleSpriteNode.registerForRendering(fill);
				bubbleTranslationNode.addChild(bubbleSpriteNode);
				graph.getRoot().addChild(bubbleTranslationNode);
			}
		}
		if(level.getCannonPosition(0) != null) {
			next.getPosition().copy(level.getCannonPosition(0));
			TranslationNode translationNode = new TranslationNode(next.getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(next);
			translationNode.addChild(spriteNode);
			graph.getRoot().addChild(translationNode);
		}
		if(level.getCannonNextPosition(0) != null) {
			nextNext.getPosition().copy(level.getCannonNextPosition(0));
			TranslationNode translationNode = new TranslationNode(nextNext.getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(nextNext);
			translationNode.addChild(spriteNode);
			graph.getRoot().addChild(translationNode);
		}
		if(renderMisc && level.getScorePosition() != null) {
			score.getPosition().copy(level.getScorePosition());
			TranslationNode translationNode = new TranslationNode(score.getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(score);
			translationNode.addChild(spriteNode);
			graph.getRoot().addChild(translationNode);
		}
		if(renderMisc && level.getClockPosition() != null) {
			clock.getPosition().copy(level.getClockPosition());
			TranslationNode translationNode = new TranslationNode(clock.getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(clock);
			translationNode.addChild(spriteNode);
			graph.getRoot().addChild(translationNode);
		}
		if(renderMisc && level.getNickPosition() != null) {
			nick.getPosition().copy(level.getNickPosition());
			TranslationNode translationNode = new TranslationNode(nick.getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(nick);
			translationNode.addChild(spriteNode);
			graph.getRoot().addChild(translationNode);
		}
		if(renderMisc && level.getResultPosition() != null) {
			result.getPosition().copy(level.getResultPosition());
			TranslationNode translationNode = new TranslationNode(result.getPosition());
			SpriteNode spriteNode = new SpriteNode();
			spriteNode.registerForRendering(result);
			translationNode.addChild(spriteNode);
			graph.getRoot().addChild(translationNode);
		}
		Renderer.getInstance().getGl().glDisable(GL.GL_TEXTURE_2D);
		for(HorizontalLine wall : level.getCriticalAreas()) {
			Renderer.getInstance().getGl().glBegin(GL.GL_LINES);
			Renderer.getInstance().getGl().glColor3f(1f, 1f, 0f);
			Renderer.getInstance().getGl().glVertex3f(-1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glVertex3f(1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glColor3f(1, 1, 1);
			Renderer.getInstance().getGl().glEnd();
		}
		for(HorizontalLine wall : level.getKillAreas()) {
			Renderer.getInstance().getGl().glBegin(GL.GL_LINES);
			Renderer.getInstance().getGl().glColor3f(0f, 0f, 0f);
			Renderer.getInstance().getGl().glVertex3f(-1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glVertex3f(1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glColor3f(1, 1, 1);
			Renderer.getInstance().getGl().glEnd();
		}
		for(HorizontalLine wall : level.getRestrictedAreas()) {
			Renderer.getInstance().getGl().glBegin(GL.GL_LINES);
			Renderer.getInstance().getGl().glColor3f(1f, 0f, 0f);
			Renderer.getInstance().getGl().glVertex3f(-1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glVertex3f(1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glColor3f(1, 1, 1);
			Renderer.getInstance().getGl().glEnd();
		}
		for(HorizontalLine wall : level.getHorizontalLines()) {
			Renderer.getInstance().getGl().glBegin(GL.GL_LINES);
			Renderer.getInstance().getGl().glColor3f(0, 1f, 0f);
			Renderer.getInstance().getGl().glVertex3f(-1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glVertex3f(1000, wall.getYPosition(), Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glColor3f(1, 1, 1);
			Renderer.getInstance().getGl().glEnd();
		}
		for(VerticalLine wall : level.getVerticalLines()) {
			Renderer.getInstance().getGl().glBegin(GL.GL_LINES);
			Renderer.getInstance().getGl().glColor3f(0, 1f, 0f);
			Renderer.getInstance().getGl().glVertex3f(wall.getXPosition(), -1000, Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glVertex3f(wall.getXPosition(), 1000, Editor.WALL_DEPTH);
			Renderer.getInstance().getGl().glColor3f(1, 1, 1);
			Renderer.getInstance().getGl().glEnd();
		}
		Renderer.getInstance().getGl().glEnable(GL.GL_TEXTURE_2D);
	}
}
